package jp.eisbahn.mgapi.api;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import jp.eisbahn.mgapi.auth.Token;
import jp.eisbahn.mgapi.http.ContentType;
import jp.eisbahn.mgapi.http.HttpFetcher;
import jp.eisbahn.mgapi.http.HttpRequest;
import jp.eisbahn.mgapi.http.HttpResponse;
import jp.eisbahn.mgapi.http.HttpUtils;

/**
 * A main class to access each mixi Graph API.
 * @author Yoichiro Tanaka
 */
public class Client {

    /** The URL for mixi Graph API. */
    private static final String URL = "http://api.mixi-platform.com/2";

    /** The fetcher object. */
    private HttpFetcher httpFetcher;

    /**
     * Set the HTTP fetcher implementation.
     * @param newHttpFetcher The implementation of HttpFetcher interface.
     */
    public final void setHttpFetcher(final HttpFetcher newHttpFetcher) {
        httpFetcher = newHttpFetcher;
    }

    /**
     * Send the request to mixi API server and return the result.
     * @param <T> The response class extended Response class.
     * @param token The token object to have the valid access token.
     * @param request The request object to access each API.
     * @return The response object.
     * @throws ApiAccessException If some errors occurred while accessing API.
     * @throws IOException If some errors about I/O occurred.
     */
    @SuppressWarnings("unchecked")
    public final <T extends Response> T send(
            final Token token, final Request request)
            throws ApiAccessException, IOException {
        HttpRequest httpRequest = createHttpRequest(token, request);
        HttpResponse httpResponse = httpFetcher.fetch(httpRequest);
        if (HttpUtils.isSuccess(httpResponse)) {
            Response response = createResponse(request);
            response.parseResponse(httpResponse);
            return (T) response;
        } else {
            throw new ApiAccessException(httpResponse);
        }
    }

    /**
     * Create and return the response instance.
     * @param request The request object.
     * @return The created response object.
     */
    private Response createResponse(final Request request) {
        try {
            Class<? extends Response> clazz = request.getResponseClass();
            Response response = clazz.newInstance();
            return response;
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        } catch (InstantiationException e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Create and return the HttpRequest object against the request object.
     * @param token The token object to have a valid access token.
     * @param request The request object for each API.
     * @return The HttpRequest object.
     */
    private HttpRequest createHttpRequest(
            final Token token, final Request request) {
        Map<String, String> headerMap = new HashMap<String, String>();
        Map<String, String> requestHeaderMap = request.getHeaderMap();
        if (requestHeaderMap != null) {
            headerMap.putAll(requestHeaderMap);
        }
        headerMap.put("Authorization", "OAuth " + token.getAccessToken());
        ContentType contentType = request.getContentType();
        HttpRequest httpRequest;
        if (contentType.equals(ContentType.JSON)
                || contentType.equals(ContentType.X_WWW_FORM_URLENCODED)) {
            httpRequest = new HttpRequest(
                URL + request.getUri(),
                request.getParameterMap(),
                request.getHttpMethod(),
                request.getContentType(),
                headerMap);
        } else {
            httpRequest = new HttpRequest(
                    URL + request.getUri(),
                    request.getBodyStream(),
                    request.getHttpMethod(),
                    request.getContentType(),
                    headerMap);
        }
        return httpRequest;
    }

}
